package com.iuie.ns.datum.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.iuie.ns.datum.service.IFileInfoService;
import com.iuie.ns.datum.service.IFileService;
import com.iuie.ns.datum.utils.WindowsCommand;
import com.iuie.ns.datum.vo.FileInfoVO;
import com.iuie.ns.frame.config.AppConfigUtil;
import com.iuie.ns.frame.exception.ServiceException;
import com.iuie.ns.frame.utils.FileUtils;
import com.iuie.ns.frame.utils.ListUtils;
import com.iuie.ns.frame.utils.StringUtils;
import com.iuie.ns.system.service.code.ISysCodeInfoService;
import com.iuie.ns.system.vo.code.SysCodeInfoVO;

@Service("fileService")
@Transactional
public class FileServiceImpl implements IFileService{

	private static final Logger logger = LoggerFactory.getLogger(FileInfoServiceImpl.class);
	
	public static final String BLOG_FILE_PATH = AppConfigUtil.getInstance().getProperty("blog.upload.path");
	
	public static final String FILE_PATH = AppConfigUtil.getInstance().getProperty("file.upload.path");
	
	public static final String MD_FILE_PATH = AppConfigUtil.getInstance().getProperty("markdown.upload.path");
	
	public static final String BOOK_FILE_PATH = AppConfigUtil.getInstance().getProperty("book.upload.path");
	
	@Resource
	private IFileInfoService fileInfoService;

	@Resource
	private ISysCodeInfoService codeInfoService;
	
	@Override
	public FileInfoVO addOneFile(MultipartFile file, Boolean immediately, String fileType, String targetId) throws IOException {
		if(file.isEmpty()){
			throw new IOException("文件内容不能为空");
		}
		String originalName = file.getOriginalFilename();
		if (originalName.length() > 200) {
			throw new ServiceException("文件上传失败，文件名过长（最大长度200），请重命名文件后上传！");
		}
		long fileSize = file.getSize();
		long fileId = System.currentTimeMillis();
		
		String fileName = null;
		if (immediately!=null && immediately==true) {
			// 立即保存-文件名为时间戳
			fileName = String.valueOf(fileId)+"."+FilenameUtils.getExtension(originalName);
		} else {
			// 非立即保存-文件名以"--"开头
			fileName = "--" + String.valueOf(fileId)+"." + FilenameUtils.getExtension(originalName);
		}
		
		FileInfoVO fileInfo = new FileInfoVO();
		fileInfo.setFileId(fileId);
		fileInfo.setOriginalName(originalName);
		fileInfo.setFileName(fileName);
		fileInfo.setUploadTime(new Date());
		fileInfo.setFileSize(fileSize);
		fileInfo.setFileType(fileType);
		fileInfo.setTargetId(targetId);
		
		fileInfoService.addOne(fileInfo);
		
		// fileType is blog or file
		String storePath = this.getPath(fileType);

		// 拷贝到E盘目录下
		if(!file.isEmpty()){
			logger.debug("Process file:{}"+file.getOriginalFilename());
			FileUtils.fileCopy(file.getInputStream(), new File(storePath,fileName));
		}
		return fileInfo;
	}
	
	@Override
	public FileInfoVO newFileWithContent(String content, String fileType, String oriFileName) throws IOException {
		if (StringUtils.isEmpty(content)) {
			throw new ServiceException("文件内容不能为空");
		}
		if (StringUtils.isEmpty(fileType)) {
			throw new ServiceException("文件类型不能为空");
		}
		FileInfoVO fileInfo = new FileInfoVO();
		long fileId = System.currentTimeMillis();
		
		// 如果文件名为空，则构造一个文件，默认文件的文件名后缀为stxt
		String originalName = oriFileName;
		String fileName = null;
		
		if (StringUtils.isEmpty(oriFileName)) {
			originalName = fileId + ".stxt";
			fileName = oriFileName;
		} else {
			fileName = fileId + "." + FilenameUtils.getExtension(originalName); // 文件id + 原始后缀名
		}
		
		fileInfo.setFileId(fileId);
		fileInfo.setOriginalName(originalName);
		fileInfo.setFileName(fileName);
		fileInfo.setFileSize(content.length());
		fileInfo.setFileType(fileType);
		fileInfo.setUploadTime(new Date());
		fileInfoService.addOne(fileInfo);
		
		// fileType is blog or file
		String storePath = getPath(fileType);
		
		// 写入文件
		File file = new File(storePath,fileName);
		FileUtils.writeStringToFile(file, content, "UTF-8");
		
		return fileInfo;
	}

	@Override
	public FileInfoVO updateFileWithContent(Long fileId, String content, String fileType, String oriFileName) throws IOException {
		if (StringUtils.isEmpty(String.valueOf(fileId))) {
			throw new ServiceException("文件标题不能为空");
		}
		if (StringUtils.isEmpty(content)) {
			throw new ServiceException("文件内容不能为空");
		}
		if (StringUtils.isEmpty(fileType)) {
			throw new ServiceException("文件类型不能为空");
		}
		FileInfoVO fileInfo = fileInfoService.findById(fileId);
		// 如果文件名为空，则构造一个文件，默认文件的文件名后缀为stxt
		String originalName = oriFileName;
		String fileName = null;
		
		if (StringUtils.isEmpty(oriFileName)) {
			originalName = fileId + ".stxt";
			fileName = oriFileName;
		} else {
			fileName = fileId + "." + FilenameUtils.getExtension(originalName); // 文件id + 原始后缀名
		}
		
		fileInfo.setFileSize(content.length());
		fileInfo.setOriginalName(originalName);
		
		// fileType is blog or file
		String storePath = getPath(fileType);
		
		// 写入文件
		File file = new File(storePath,fileName);
		logger.info("md blog path: {}", file.getAbsolutePath());
		FileUtils.writeStringToFile(file, content, "UTF-8");
		fileInfoService.updateOne(fileInfo);
		
		return fileInfo;
	}
	
	@Override
	public void deleteFileById(Serializable fileId) {
		this.deleteFileById(fileId, false);
	}
	
	@Override
	public void deleteFileById(Serializable fileId, boolean deleteTag) {
		FileInfoVO fileInfo = fileInfoService.findById(fileId);
		if(fileInfo == null){
			return;
		}
		String path = getPath(fileInfo.getFileType());
		boolean flag = false;
		File f = new File(path,fileInfo.getFileName());
		
		boolean exists = f.exists();
		if(exists){
			flag = f.delete();
		}
		// 文件删除成功再删除文件信息
		if (flag || deleteTag || !exists) { // 文件不存在，直接删除文件信息
			fileInfoService.deleteById(fileId);
			
		}
	}
	
	@Override
	public void deleteFileByTargetId(String targetId) {
		// 根据targetId查询出fileInfos，然后遍历删除
		List<FileInfoVO> list = fileInfoService.findByTargetId(targetId);
		if (ListUtils.isNotEmpty(list)) {
			for (FileInfoVO fileInfoItem: list) {
				this.deleteFileById(fileInfoItem.getFileId(), true);
			}
		}
		// 根据targetId删除文件信息
		fileInfoService.deleteByTargetId(targetId);
	}
	
	/**
	 * 根据文件类型取路径
	 * @param fileType
	 * @return
	 * @date 2017年8月15日 下午3:31:50
	 */
	private String getPath(String fileType) {
		SysCodeInfoVO codeInfo = codeInfoService.findCodeInfoByCodeValueAndCodeType(fileType, "fileTypeWithFilePathRelation");
	    return codeInfo.getCodeInfoName();
	}

	@Deprecated
	@Override
	public ResponseEntity<byte[]> fileDownload(Serializable fileId,Boolean download) throws IOException {
		if(download==null){
			download=Boolean.TRUE;
		}
		FileInfoVO fileInfo = fileInfoService.findById(fileId);
		File f = new File(FILE_PATH, fileInfo.getFileName());
		if (f.exists()) {
			fileInfoService.downloadFileById(fileId);
		}
		
        String fileName=new String(fileInfo.getOriginalName().getBytes("UTF-8"),"iso-8859-1");//为了解决中文名称乱码问题
        HttpHeaders headers = new HttpHeaders();    
        if(download){
	        headers.setContentDispositionFormData("attachment", fileName);  
	        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        }
        byte[] buffer = FileUtils.readFileToByteArray(f);
        return new ResponseEntity<byte[]>(buffer,headers,HttpStatus.CREATED);
	}

	@Override
	public void fileDownload(Serializable fileId, Boolean download, HttpServletResponse resp) throws UnsupportedEncodingException {
		if(download==null){
			download=Boolean.TRUE;
		}
		FileInfoVO fileInfo = fileInfoService.findById(fileId);
		String path = getPath(fileInfo.getFileType());
		File f = new File(path, fileInfo.getFileName());
		
		if (f.exists()) {
			fileInfoService.downloadFileById(fileId);
		}
		if(download){
			String fileName=fileInfo.getOriginalName(); 
			resp.setCharacterEncoding("utf-8");
			resp.setContentType("application/octet-stream");
			String fileNameUrl = URLEncoder.encode(fileName,"UTF-8");
			resp.setHeader("Content-Disposition", "form-data; name=\"attachment\";filename=\""+ fileNameUrl+"\"");
		}
		FileInputStream fis = null;
		OutputStream os = null;
		try {
			fis = new FileInputStream(f);
			os = resp.getOutputStream();
			byte[] buffer = new byte[1024];
			int length;
			while((length = fis.read(buffer))>0){
				os.write(buffer, 0, length);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
				if (os != null) {
					os.flush();
					os.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void letFileNameLegal(Serializable fileId) throws IOException {
		FileInfoVO fileInfo = fileInfoService.loadById(fileId);
		if (fileInfo == null) {
			throw new NullPointerException("系统中不能找到该文件的相关信息：fileId=" + fileId);
		}
		String fileName = fileInfo.getFileName();
		String fileType = fileInfo.getFileType();
		SysCodeInfoVO codeInfo = codeInfoService.findCodeInfoByCodeValueAndCodeType(fileType, "fileTypeWithFilePathRelation");
		String basePath = codeInfo.getCodeInfoName();
		File oldFile = new File(basePath,fileInfo.getFileName());
		if (!oldFile.exists()) {
			throw new IOException("系统找不到文件：fileName=" + fileName);
		}
		if(fileName!=null && fileName.startsWith("--")){
			fileName = fileName.substring(2);
		}
		File newFile = new File(basePath,fileName);
		if (!oldFile.renameTo(newFile)) {
			throw new IOException("文件名修改失败：fileName=" + fileName);
		}                                                                                                                                                                                                                                  
		fileInfo.setFileName(fileName);
	}

	@Override
	public void updateFileSizeAuto(Long fileId) {
		FileInfoVO fileInfo = fileInfoService.loadById(fileId);
		if (fileInfo == null) {
			throw new NullPointerException("系统中不能找到该文件的相关信息：fileId=" + fileId);
		}
		String fileName = fileInfo.getFileName();
		File file = new File(FILE_PATH, fileName);
		long fileSize = file.length();
		fileInfo.setFileSize(fileSize);
	}
	
	@Override
	public void openFolder(Long fileId) throws Exception {
		FileInfoVO fileInfo = fileInfoService.loadById(fileId);
		if (fileInfo == null) {
			throw new NullPointerException("系统中不能找到该文件的相关信息：fileId=" + fileId);
		}
		if (!StringUtils.equals(fileInfo.getFileType(), "file")) {
			throw new ServiceException("打开文件夹失败，不支持打开文件类型为【" + fileInfo.getFileType() + "】的文件夹！");
		}
		SysCodeInfoVO codeInfo = codeInfoService.findCodeInfoByCodeValueAndCodeType("fileNativeTag", "sysParams");
		if (codeInfo == null || StringUtils.equals(codeInfo.getCodeInfoName(), "0")) {
			throw new ServiceException("打开文件夹失败，系统不支持，请联系管理员查明原因！");
		}
		
		String fullFileName = FILE_PATH + fileInfo.getFileName();
		String command = "openfolder?\"" + fullFileName + "\"";
		logger.info("open folder, command: {}", command);
		WindowsCommand.getWindowsCommand().execute(command);
	}
	
	@Override
	public void openFile(Long fileId) {
		FileInfoVO fileInfo = fileInfoService.loadById(fileId);
		if (fileInfo == null) {
			throw new NullPointerException("系统中不能找到该文件的相关信息：fileId=" + fileId);
		}
		if (!StringUtils.equals(fileInfo.getFileType(), "file")) {
			throw new ServiceException("打开文件失败，不支持打开文件类型为【" + fileInfo.getFileType() + "】的文件夹！");
		}
		SysCodeInfoVO codeInfo = codeInfoService.findCodeInfoByCodeValueAndCodeType("fileNativeTag", "sysParams");
		if (codeInfo == null || StringUtils.equals(codeInfo.getCodeInfoName(), "0")) {
			throw new ServiceException("打开文件失败，系统不支持，请联系管理员查明原因！");
		}
		
		String fullFileName = FILE_PATH + fileInfo.getFileName();
		String command = "openfile?" + fullFileName;
		logger.info("open file, command: {}", command);
		WindowsCommand.getWindowsCommand().execute(command);
	}
}
